/**
 * 
 */
package com.yishuki.bluewhale.db.access;

import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.yishuki.bluewhale.Constant;
import com.yishuki.bluewhale.db.entity.Category;
import com.yishuki.bluewhale.db.entity.CategoryProduction;
import com.yishuki.bluewhale.db.entity.Production;
import com.yishuki.bluewhale.db.entity.ProductionImage;

import name.yzhu.database.DataBaseException;
import name.yzhu.database.IDbUtil;

/**
 * production and category operation class . it includes but not limit the features below:
 * <pre>
 * <b>add production</b><br>
 * <b>update production</b><br>
 * <b>delete production</b> <br>
 * </pre>
 * <b>NOTE</b>: it is not a thread safely<p>
 * @author yhzhu
 *
 */
public class ProductionOpr extends AbstractDBOpr{
	private static final Log LOG = LogFactory.getLog(ProductionOpr.class);
	
	public ProductionOpr(IDbUtil db) {
		super(db);
	}
	/**
	 * add category and but it is not committed to db immediately
	 * @param category
	 * @throws DataBaseException
	 */
	public void addCategory(Category category) throws DataBaseException{
		addCategory(category, false);
	}
	/**
	 * add category
	 * @param category
	 * @param isCommitted  true if it is committed to db immediately after adding otherwise false 
	 * @throws DataBaseException
	 */
	public void addCategory(Category category, boolean isCommitted) throws DataBaseException{
		try {
			db.save(category);
			if (isCommitted)
			    db.flush();
		} catch (DataBaseException e) {
			if (LOG.isErrorEnabled())
				LOG.debug(e.getMessage(), e);
			throw e;
		}
	}
	/**
	 * update category but it is not committed to db immediately
	 * @param oldCategory
	 * @param newCategory
	 * @throws DataBaseException
	 */
	public void updateCategory(Category category) throws DataBaseException{
		updateCategory(category, false);
	}
	/**
	 * update category
	 * @param category
	 * @param isCommitted  true if it is committed to db immediately after adding otherwise false 
	 * @throws DataBaseException
	 */
	public void updateCategory(Category category, boolean isCommitted) throws DataBaseException{
		try {
//			oldCategory.setName(newCategory.getName());
//			oldCategory.setDescription(newCategory.getDescription());
//			oldCategory.setParentId(newCategory.getParentId());
			db.update(category);
			if (isCommitted)
			    db.flush();
		} catch (DataBaseException e) {
			if (LOG.isErrorEnabled())
				LOG.debug(e.getMessage(), e);
			throw e;
		}
	}
	/**
	 * delete category but it is not committed to db immediately<br>
	 * the DataBaseException would be throw 
	 *  if there are some subcategory or productions in the category
	 * @param category
	 * @throws DataBaseException
	 */
	public void deleteCategory(Category category) throws DataBaseException{
		deleteCategory(category, false);
	}
	/**
	 * delete category ,the DataBaseException would be throw 
	 *  if there are some subcategory or productions in the category
	 * @param category
	 * @param isCommitted  true if it is committed to db immediately after adding otherwise false 
	 * @throws DataBaseException 
	 */
	public void deleteCategory(Category category, boolean isCommitted) throws DataBaseException{
		try {
			db.Delete(category);
			if (isCommitted)
			    db.flush();
		} catch (DataBaseException e) {
			if (LOG.isErrorEnabled())
				LOG.debug(e.getMessage(), e);
			throw e;
		}
	}
	/**
	 * add production and add the relation of between production and category 
	 * but it is not committed to db immediately
	 * @param production
	 * @param category
	 * @throws DataBaseException
	 */
	public void addProduction(Production production, List<Category> category) throws DataBaseException{
		addProduction(production, category, false);
	}
	/**
	 * add production and add the relation of between production and category<br> 
	 * the flush should be called when it need to be committed to db if isCommitted is false
	 * @param production  new production
	 * @param category
	 * @param isCommitted  true if it is committed to db immediately after adding otherwise false 
	 * @throws DataBaseException
	 * @see  flush
	 */
	public void addProduction(Production production, List<Category> category, boolean isCommitted) throws DataBaseException{
		try {
			db.save(production);
			for (Category c : category){
				CategoryProduction cp = new CategoryProduction(c.getCategoryId(), 
						production.getProductionId());
				db.save(cp);
			}
			if (isCommitted)
			    db.flush();
		} catch (DataBaseException e) {
			if (LOG.isErrorEnabled())
				LOG.debug(e.getMessage(), e);
			throw e;
		}
	}
	/**
	 * update production but it is not committed to db immediately
	 * @param oldProduction
	 * @param newProduction
	 * @throws DataBaseException
	 * @see flush
	 */
	public void updateProduction(Production production) throws DataBaseException{
		updateProduction(production, false);
	}
	/**
	 * update production <br>
	 * the flush should be called when it need to be committed to db if isCommitted is false
	 * @param oldProduction
	 * @param newProduction
	 * @param isCommitted  true if it is committed to db immediately after updating otherwise false
	 * @throws DataBaseException 
	 */
	public void updateProduction(Production production, boolean isCommitted) throws DataBaseException{
//		oldProduction.setName(newProduction.getName());
//		oldProduction.setDescription(newProduction.getDescription());
		try {
			db.update(production);
			if (isCommitted)
			    db.flush();
		} catch (DataBaseException e) {
			if (LOG.isErrorEnabled())
				LOG.debug(e.getMessage(), e);
			throw e;
		}
	}
	/**
	 * update production with category but it is not committed to db immediately
	 * @param oldProduction
	 * @param newProduction
	 * @param category
	 * @throws DataBaseException
	 */
	public void updateProduction(Production production, 
			List<Category> category) throws DataBaseException{
		updateProduction(production, category, false);
	}
	/**
	 * update production with category
	 * @param oldProduction
	 * @param newProduction
	 * @param category new category list
	 * @param isCommitted true if it is committed to db immediately after updating otherwise false
	 * @throws DataBaseException
	 * @see flush
	 */
	public void updateProduction(Production production, 
			List<Category> category, boolean isCommitted) throws DataBaseException{
//		oldProduction.setName(newProduction.getName());
//		oldProduction.setDescription(newProduction.getDescription());
		try {
			String hql = "Delete CategoryProduction where productionId=?";
			db.saveOrUpdateOrDelForHQL(hql, production.getProductionId().toString());
			
			db.update(production);
			
			for (Category c : category){
				CategoryProduction cp = new CategoryProduction(c.getCategoryId(), 
						production.getProductionId());
				db.save(cp);
			}
			if (isCommitted)
			    db.flush();
		} catch (DataBaseException e) {
			if (LOG.isErrorEnabled())
				LOG.debug(e.getMessage(), e);
			throw e;
		}
	}
	/**
	 * delete production and delete the relation of between production and category  
	 * but it is not committed to db immediately
	 * @param production
	 * @throws DataBaseException
	 */
	public void deleteProduction(Production production) throws DataBaseException{
		deleteProduction(production, false);
	}
	/**
	 * delete production and delete the relation of between production and category
	 * @param production
	 * @param isCommitted true if it is committed to db immediately after deleting otherwise false
	 * @throws DataBaseException 
	 */
	public void deleteProduction(Production production,
			boolean isCommitted) throws DataBaseException{
		try {
			String hql = "Delete CategoryProduction where productionId=?";
			db.saveOrUpdateOrDelForHQL(hql, production.getProductionId().toString());
			hql = "Delete ProductionImage where productionId=?";
			db.saveOrUpdateOrDelForHQL(hql, production.getProductionId().toString());
			db.Delete(production);
			if (isCommitted)
			    db.flush();
		} catch (DataBaseException e) {
			if (LOG.isErrorEnabled())
				LOG.debug(e.getMessage(), e);
			throw e;
		}
	}
	/**
	 * add image for production
	 * @param production
	 * @param images batch add production image and the images must need to be 
	 * sorted by display
	 * @throws DataBaseException 
	 */
	public void addProductionImage(Production production, byte[][] images) throws DataBaseException{
		if (images == null || images.length == 0)
			throw new NullPointerException("image is null");
		try{
			for (int i=0; i<images.length && i<Constant.MAX_NUMBER_OF_PRODCTION_IMAGE; i++){
				if (images[i] == null)
					continue;
				ProductionImage pi = new ProductionImage();
				pi.setImage(images[i]);
				pi.setOrder(i);
				pi.setProductionId(production.getProductionId());
				db.save(pi);
			}
		} catch (DataBaseException e) {
			if (LOG.isErrorEnabled())
				LOG.debug(e.getMessage(), e);
			throw e;
		}
	}
	/**
	 * batch update production images
	 * @param pis
	 * @throws DataBaseException
	 */
	public void updateProductionImage(List<ProductionImage> pis) throws DataBaseException{
		try{
			for (ProductionImage  pi: pis)
				db.save(pi);
		} catch (DataBaseException e) {
			if (LOG.isErrorEnabled())
				LOG.debug(e.getMessage(), e);
			throw e;
		}
	}
	/**
	 * delete production images
	 * @param pis
	 * @throws DataBaseException
	 */
	public void deleteProductionImage(List<ProductionImage> pis) throws DataBaseException{
		try{
			for (ProductionImage  pi: pis){
				if (pi.getImageId() != null){
				    db.Delete(pi);
				}
				else {
					if (pi.getProductionId() == null || pi.getOrder() == null)
						throw new IllegalArgumentException("Must need to specify both production id and image order");
					String hql = "Delete ProductionImage where productionId=? " +
							" and order=?";
					String[] params = new String[] {pi.getProductionId().toString(), 
							pi.getOrder().toString()};
					db.saveOrUpdateOrDelForHQL(hql, params);
				}
			}
		} catch (DataBaseException e) {
			if (LOG.isErrorEnabled())
				LOG.debug(e.getMessage(), e);
			throw e;
		}
	}
}
